/*
* Copyright (c) Huawei Technologies Co., Ltd. 2012-2018. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// Created by wang-bain on 2021/11/11.


#include "utils.h"

using namespace std;

// 获取时间用于打印日志
string GetTime()
{
    struct timeval timeEval{};
    gettimeofday(&timeEval, nullptr);
    int milliSecond = int(timeEval.tv_usec / 1000);

    time_t timeStamp;
    time(&timeStamp);
    char secondTime[20];
    strftime(secondTime, sizeof(secondTime), "%Y-%m-%d %H:%M:%S", localtime(&timeStamp));

    char milliTime[24];
    snprintf(milliTime, sizeof(milliTime), "%s.%03d", secondTime, milliSecond);

    return milliTime;
}

// 格式化Int字符串, 将整型拼成formatSize长度的字符串. 如formatSize=3, 则拼成001, 002, 003...
string FormatInt(string Idx, size_t formatSize)
{
    size_t sizeIdx = Idx.size();
    if (sizeIdx < formatSize) {
        for (size_t i = 0; i < formatSize - sizeIdx; ++i) {
            Idx = "0" + Idx;
        }
    }
    return Idx;
}

// 获取输入文件名, 用于拼接输出文件名
string GetInputName(const string &inputFile)
{
    vector<string> v_inputNameBin;
    Split(inputFile, v_inputNameBin, "/");
    string inputNameBin = v_inputNameBin[v_inputNameBin.size() - 1];
    vector<string> v_inputName;
    Split(inputNameBin, v_inputName, ".");
    return v_inputName[0];
}

// 判断输入是文件还是目录
string JudgeInputType(const vector<string> &v_inputItems)
{
    size_t dirNumber = 0;
    struct stat s_stat{};
    for (auto &v_inputItem: v_inputItems) {
        if (stat((char *) v_inputItem.c_str(), &s_stat) == 0) {
            if (s_stat.st_mode & S_IFDIR) dirNumber += 1;
        }
    }
    // 目录输入
    if (dirNumber == v_inputItems.size()) return "directory";
    // 文件输入
    if (dirNumber == 0) return "file";
    // 异常输入
    return "empty";
}

// 打屏帮助信息
void Help()
{
    printf("%s - INFO - [XACL]: Usage: ./xacl_fmk [parameters]\n"
           "-m=model                 Required, om model file path. Relative and absolute paths are supported.\n"
           "\n"
           "-i=inputFiles            Optional, input files or directories. Use commas to separate each inputs.\n"
           "                         Set inputs to zeros if not specified. Relative and absolute paths are supported.\n"
           "-o=outputPath            Optional, path of output files. The default value is the current path.\n"
           "                         Relative and absolute paths are supported.\n"
           "\n"
           "-v=dynamicShape          Optional, dynamic size of shape.\n"
           "                         Use semicolon to separate each input, use commas to separate each dim.\n"
           "                         The default value is NULL, indicating that dynamicShape function is disabled.\n"
           "                         Enter the actual shape size when dynamicShape function is enabled.\n"
           "-x=imageRank             Optional, rank size of image. Use commas to separate height and width.\n"
           "                         The default value is NULL, indicating that imageRank function is disabled.\n"
           "                         Enter the actual height and width size when imageRank function is enabled.\n"
           "-y=batchRank             Optional, rank size of batch.\n"
           "                         The default value is NULL, indicating that batchRank function is disabled.\n"
           "                         Enter the actual size of the batch when batchRank function is enabled.\n"
           "-z=dimsRank              Optional, rank size of dims. Use commas to separate each dim.\n"
           "                         The default value is NULL, indicating that dimsRank function is disabled.\n"
           "                         Enter the actual size of each dims when dimsRank function is enabled.\n"
           "\n"
           "-n=nodeId                Optional, ID of the Ascend device used for inference.\n"
           "                         The default value is 0, indicating that device 0 is used for inference.\n"
           "-l=loopNum               Optional, the number of inference times.\n"
           "                         The default value is 1, indicating that inference is performed once.\n"
           "-d=dumpJson              Optional, configuration file used to save operator data or profiling.\n"
           "                         The default value is NULL, indicating that the save function is disabled.\n"
           "\n"
           "-h=help                  Show this help message.\n", GetTime().c_str());
}

// 获取硬件状态, 包含功率, 温度, AICore频率
void GetHardwareStatus(map<char, string> &params)
{
    int ret;
    int nodeId = (int) strtol(params['n'].c_str(), nullptr, 10);
    string socName = aclrtGetSocName();

    string socMem;
    struct dsmi_memory_info_stru s_memInfo = {0};
    ret = dsmi_get_memory_info(nodeId, &s_memInfo);
    if (ret != SUCCESS) {
        socMem = "N/A";
    } else {
        unsigned long utilization = s_memInfo.utiliza * s_memInfo.memory_size;
        if (socName == "Ascend310") {
            socMem = to_string(utilization / 100);
        } else {
            socMem = to_string(utilization / 102400);
        }
    }
    string socTemp;
    int temperature;
    ret = dsmi_get_device_temperature(nodeId, &temperature);
    if (ret != SUCCESS) {
        socTemp = "N/A";
    } else {
        socTemp = to_string(temperature);
    }
    string devicePower;
    struct dsmi_power_info_stru s_powerInfo = {0};
    ret = dsmi_get_device_power_info(nodeId, &s_powerInfo);
    if (ret != SUCCESS) {
        devicePower = "N/A";
    } else {
        devicePower = to_string(s_powerInfo.power * 0.1);
    }
    string AICoreFreq;
    struct dsmi_aicore_info_stru s_AICoreFreq = {0};
    ret = dsmi_get_aicore_info(nodeId, &s_AICoreFreq);
    if (ret != SUCCESS) {
        AICoreFreq = "N/A";
    } else {
        AICoreFreq = to_string(s_AICoreFreq.curfreq);
    }
    printf("%s - INFO - [XACL]: Current Memory: %s MB, Power: %s W, SoC Temperature: %s C, AICore Frequency: %s MHz\n",
           GetTime().c_str(), socMem.c_str(), devicePower.c_str(), socTemp.c_str(), AICoreFreq.c_str());
}

// 将字符串按delimiters字符拆分成vector
void Split(const string &tokens, vector<string> &v_tokens, const string &delimiters = ";")
{
    string::size_type lastPos = tokens.find_first_not_of(delimiters, 0);
    string::size_type pos = tokens.find_first_of(delimiters, lastPos);
    while (string::npos != pos || string::npos != lastPos) {
        v_tokens.push_back(tokens.substr(lastPos, pos - lastPos));
        lastPos = tokens.find_first_not_of(delimiters, pos);
        pos = tokens.find_first_of(delimiters, lastPos);
    }
}

// 扫描并获取目录下所有输入文件
void ScanInputFiles(vector<string> &v_fileList, const string &inputDirectory)
{
    const char *str = inputDirectory.c_str();
    DIR *dir = opendir(str);
    struct dirent *p;
    while ((p = readdir(dir)) != nullptr) {
        string fileName = inputDirectory;
        if (p->d_name[0] != '.') {
            string name = string(p->d_name);
            v_fileList.push_back(fileName.append("/").append(name));
        }
    }
    closedir(dir);
}

// 判断三种输入场景, 生成统一的输入列表v_allInputFiles
void GenerateInputList(struct ModelInfo &modelInfo, map<char, string> &params)
{
    Split(params['i'], modelInfo.v_inputItems, ",");
    if (!modelInfo.v_inputItems.empty()) {
        // 输入非空
        string inputType = JudgeInputType(modelInfo.v_inputItems);
        if (inputType == "directory") {
            // 输入是目录, 扫描全部输入文件, 压入v_allInputFiles
            printf("%s - INFO - [XACL]: Input type is directory\n",
                   GetTime().c_str());
            params['t'] = inputType;
            for (auto &v_inputItem: modelInfo.v_inputItems) {
                vector<string> v_fileName;
                ScanInputFiles(v_fileName, v_inputItem);
                // 如果是空目录, 则作为零输入处理
                if (v_fileName.empty()) {
                    printf("%s - WARNING - [XACL]: No file in the directory: %s, execute as empty input\n",
                           GetTime().c_str(), v_inputItem.c_str());
                    params['t'] = "empty";
                    modelInfo.v_allInputFiles.push_back(v_fileName);
                    continue;
                }
                // 排序输入文件
                sort(v_fileName.begin(), v_fileName.end());
                modelInfo.v_allInputFiles.push_back(v_fileName);
            }
        } else if (inputType == "file") {
            // 输入是文件, 将单个文件压入v_allInputFiles
            params['t'] = inputType;
            printf("%s - INFO - [XACL]: Input type is files\n",
                   GetTime().c_str());
            for (auto &v_inputItem: modelInfo.v_inputItems) {
                vector<string> v_fileName;
                v_fileName.push_back(v_inputItem);
                modelInfo.v_allInputFiles.push_back(v_fileName);
            }
        } else {
            // 异常输入(不全是目录输入), 则作为零输入处理
            printf("%s - WARNING - [XACL]: Inputs contain both files and directories, execute as empty input\n",
                   GetTime().c_str());
            params['t'] = inputType;
            string zeroFileName;
            vector<string> v_fileName;
            v_fileName.push_back(zeroFileName);
            modelInfo.v_allInputFiles.push_back(v_fileName);
        }
    } else {
        // 输入为空, 将空字符串压入v_allInputFiles
        params['t'] = "empty";
        printf("%s - INFO - [XACL]: Input type is empty\n",
               GetTime().c_str());
        string zeroFileName;
        vector<string> v_fileName;
        v_fileName.push_back(zeroFileName);
        modelInfo.v_allInputFiles.push_back(v_fileName);
    }
}

// 销毁ACL
void DestroyACL()
{
    aclError ret;
    printf("%s - INFO - [XACL]: Start to finalize acl, aclFinalize interface adds 2s delay to upload device logs\n",
           GetTime().c_str());
    ret = aclFinalize();
    if (ret != ACL_ERROR_NONE) {
        printf("%s - ERROR - [XACL]: Interface of aclFinalize return failed, error message is:\n%s\n",
               GetTime().c_str(), aclGetRecentErrMsg());
    }
    printf("%s - INFO - [XACL]: Finalize acl success\n",
           GetTime().c_str());
}

// 释放Device
void DestroyDevice(map<char, string> &params)
{
    aclError ret;
    int nodeId = (int) strtol(params['n'].c_str(), nullptr, 10);
    ret = aclrtResetDevice(nodeId);
    if (ret != ACL_ERROR_NONE) {
        printf("%s - ERROR - [XACL]: Interface of aclrtResetDevice return failed, error message is:\n%s\n",
               GetTime().c_str(), aclGetRecentErrMsg());
    }
}

// 释放模型上下文
void DestroyContext(struct ModelInfo &modelInfo)
{
    aclError ret;
    for (auto &iter: modelInfo.v_context) {
        ret = aclrtDestroyContext(iter);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclrtDestroyContext return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
        }
    }
}

// 销毁模型
void DestroyModel(struct ModelInfo &modelInfo)
{
    aclError ret;
    ret = aclmdlUnload(modelInfo.modelId);
    if (ret != ACL_ERROR_NONE) {
        printf("%s - ERROR - [XACL]: Interface of aclmdlUnload return failed, error message is:\n%s\n",
               GetTime().c_str(), aclGetRecentErrMsg());
    }
}

// 销毁模型描述
void DestroyModelDesc(struct ModelInfo &modelInfo)
{
    aclError ret;
    ret = aclmdlDestroyDesc(modelInfo.modelDesc);
    if (ret != ACL_ERROR_NONE) {
        printf("%s - ERROR - [XACL]: Interface of aclmdlDestroyDesc return failed, error message is:\n%s\n",
               GetTime().c_str(), aclGetRecentErrMsg());
    }
    modelInfo.modelDesc = nullptr;
}

// 销毁Dataset
void DestroyDataset(aclmdlDataset *dataset)
{
    aclError ret;
    for (size_t i = 0; i < aclmdlGetDatasetNumBuffers(dataset); ++i) {
        aclDataBuffer *dataBuffer = aclmdlGetDatasetBuffer(dataset, i);
        void *data = aclGetDataBufferAddr(dataBuffer);

        ret = aclrtFree(data);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclrtFree return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
        }

        ret = aclDestroyDataBuffer(dataBuffer);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclDestroyDataBuffer return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
        }
    }

    ret = aclmdlDestroyDataset(dataset);
    if (ret != ACL_ERROR_NONE) {
        printf("%s - ERROR - [XACL]: Interface of aclmdlDestroyDataset return failed, error message is:\n%s\n",
               GetTime().c_str(), aclGetRecentErrMsg());
    }
    printf("%s - INFO - [XACL]: Destroy dataset success\n",
           GetTime().c_str());
}

// 销毁输入描述
void DestroyInputDesc(struct ModelInfo &modelInfo)
{
    for (auto &inputDesc: modelInfo.v_inputDesc) {
        aclDestroyTensorDesc(inputDesc);
    }
    vector<aclTensorDesc *>().swap(modelInfo.v_inputDesc);
}

// 销毁输入输出
void ResetDataset(struct ModelInfo &modelInfo)
{
    // Fix bugs: 每个Step后需要销毁Input和Output
    if (!modelInfo.v_inputDesc.empty()) {
        DestroyInputDesc(modelInfo);
    }
    if (modelInfo.input != nullptr) {
        DestroyDataset(modelInfo.input);
        modelInfo.input = nullptr;
    }
    if (modelInfo.output != nullptr) {
        DestroyDataset(modelInfo.output);
        modelInfo.output = nullptr;
    }
}

// AICoreError异常回调函数
void ExceptionCallBack(aclrtExceptionInfo *exceptionInfo)
{
    printf("%s - ERROR - [XACL]: AIC ERROR has been detected, exception callback is starting\n",
           GetTime().c_str());
    uint32_t nodeId = aclrtGetDeviceIdFromExceptionInfo(exceptionInfo);
    uint32_t streamId = aclrtGetStreamIdFromExceptionInfo(exceptionInfo);
    uint32_t taskId = aclrtGetTaskIdFromExceptionInfo(exceptionInfo);

    size_t opNameLen = 256;
    char opName[opNameLen];
    aclTensorDesc *inDesc = nullptr;
    aclTensorDesc *outDesc = nullptr;
    size_t inCnt = 0;
    size_t outCnt = 0;
    aclError ret;

    // 当发生异常回调时触发线程处理函数, 将需要的算子信息打屏
    ret = aclmdlCreateAndGetOpDesc(nodeId, streamId, taskId, opName, opNameLen, &inDesc, &inCnt, &outDesc, &outCnt);
    if (ret != ACL_ERROR_NONE) {
        printf("%s - ERROR - [XACL]: Interface of aclmdlCreateAndGetOpDesc return failed, error message is:\n%s\n",
               GetTime().c_str(), aclGetRecentErrMsg());
    } else {
        printf("%s - ERROR - [XACL]: Device ID: %d, Stream ID: %d, Task ID: %d, Operator Name: %s\n",
               GetTime().c_str(), nodeId, streamId, taskId, opName);
        // 此处仅打屏算子输入输出地址信息, 可以调用aclTensorDesc相关接口, 按需打印其他信息, 如Format, Type, Size等
        for (size_t i = 0; i < inCnt; ++i) {
            const aclTensorDesc *tensorDesc = aclGetTensorDescByIndex(inDesc, i);
            void *tensorAddress = aclGetTensorDescAddress(tensorDesc);
            if (tensorAddress != nullptr) {
                printf("%s - ERROR - [XACL]: Operator %s input %ld address: %p\n",
                       GetTime().c_str(), opName, i, tensorAddress);
            }
        }
        for (size_t i = 0; i < outCnt; ++i) {
            const aclTensorDesc *tensorDesc = aclGetTensorDescByIndex(outDesc, i);
            void *tensorAddress = aclGetTensorDescAddress(tensorDesc);
            if (tensorAddress != nullptr) {
                printf("%s - ERROR - [XACL]: Operator %s output %ld address: %p\n",
                       GetTime().c_str(), opName, i, tensorAddress);
            }
        }
        aclDestroyTensorDesc(inDesc);
        aclDestroyTensorDesc(outDesc);
    }
}

// 根据-o参数判断目录是否存在, 不存在则创建目录
Result CreateDir(string &pathName)
{
    string tempPath;
    vector<string> v_pathName;
    if (pathName.find("./") == 0) {
        tempPath = "./";
    } else if (pathName.find('/') == 0) {
        tempPath = "/";
    }

    Split(pathName, v_pathName, "/");

    for (auto &i: v_pathName) {
        if (i == ".") continue;
        tempPath += i + "/";
        if (access(tempPath.c_str(), F_OK) == -1) {
            if (mkdir(tempPath.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) {
                printf("%s - ERROR - [XACL]: Create path failed\n",
                       GetTime().c_str());
                return FAILED;
            }
        }
    }
    pathName = tempPath;
    return SUCCESS;
}

// 校验入参
Result InitAndCheckParams(int argc, char *argv[], map<char, string> &params)
{
    // 设置默认值
    params['m'] = "";
    params['i'] = "";
    params['o'] = "./";
    params['d'] = "";
    params['v'] = "";
    params['x'] = "";
    params['y'] = "";
    params['z'] = "";
    params['n'] = "0";
    params['l'] = "1";
    // 获取入参
    while (true) {
        int optionIdx = 0;
        int optionInput;
        struct option Options[] = {{"modelFile",    1, nullptr, 'm'},
                                   {"inputFiles",   1, nullptr, 'i'},
                                   {"outputPath",   1, nullptr, 'o'},
                                   {"dynamicShape", 1, nullptr, 'v'},
                                   {"imageRank",    1, nullptr, 'x'},
                                   {"batchRank",    1, nullptr, 'y'},
                                   {"dimsRank",     1, nullptr, 'z'},
                                   {"nodeId",       1, nullptr, 'n'},
                                   {"loopNum",      1, nullptr, 'l'},
                                   {"dumpJson",     1, nullptr, 'd'},
                                   {"help",         0, nullptr, 'h'},
        };

        optionInput = getopt_long(argc, argv, "m:i:o:v:x:y:z:n:l:d:h", Options, &optionIdx);
        if (optionInput == -1) {
            break;
        }
        switch (optionInput) {
            case 'm': {
                // 模型文件: 必填参数
                params['m'] = string(optarg);
                printf("%s - INFO - [XACL]: Inference model file: %s\n",
                       GetTime().c_str(), params['m'].c_str());
                break;
            }

            case 'i': {
                // 模型输入: 可选参数. 默认为空, 支持文件输入和目录输入, 动态Shape场景下必填
                params['i'] = string(optarg);
                printf("%s - INFO - [XACL]: Inputs: %s\n",
                       GetTime().c_str(), params['i'].c_str());
                break;
            }

            case 'o': {
                // 模型输出: 可选参数. 默认当前目录, 支持自动创建目录
                params['o'] = string(optarg);
                printf("%s - INFO - [XACL]: Outputs path: %s\n",
                       GetTime().c_str(), params['o'].c_str());
                break;
            }

            case 'v': {
                // 动态Shape: 可选参数. 输入Shape列表, 多输入以分号(;)分割不同输入, 每个输入以逗号(,)分割不同维度
                params['v'] = string(optarg);
                printf("%s - INFO - [XACL]: Dynamic size of shape: %s\n",
                       GetTime().c_str(), params['v'].c_str());
                break;
            }

            case 'x': {
                // 动态分辨率分档: 可选参数. 动态分辨率场景下输入H和W值, 以逗号(,)分割, 输入顺序必须是先H后W
                params['x'] = string(optarg);
                printf("%s - INFO - [XACL]: Rank size of image: %s\n",
                       GetTime().c_str(), params['x'].c_str());
                break;
            }

            case 'y': {
                // 动态Batch分档: 可选参数. 动态Batch分档下的BatchSize值
                params['y'] = string(optarg);
                printf("%s - INFO - [XACL]: Rank size of batch: %s\n",
                       GetTime().c_str(), params['y'].c_str());
                break;
            }

            case 'z': {
                // 动态Dims分档: 可选参数. 动态Dims分档下所有输入Dims列表, 以逗号(,)分割所有输入维度
                params['z'] = string(optarg);
                printf("%s - INFO - [XACL]: Rank size of dims: %s\n",
                       GetTime().c_str(), params['z'].c_str());
                break;
            }

            case 'n': {
                // Device ID: 可选参数. 默认使用Device 0
                params['n'] = string(optarg);
                printf("%s - INFO - [XACL]: Ascend device id: %s\n",
                       GetTime().c_str(), params['n'].c_str());
                break;
            }

            case 'l': {
                // 单样本循环次数: 可选参数. 默认单个样本执行1次. 用于单样本循环取平均性能的场景
                params['l'] = string(optarg);
                printf("%s - INFO - [XACL]: Inference loops: %s\n",
                       GetTime().c_str(), params['l'].c_str());
                break;
            }

            case 'd': {
                // Dump开关: 可选参数. 支持保存单算子输入输出数据或模型性能数据
                params['d'] = string(optarg);
                printf("%s - INFO - [XACL]: Dump config file: %s\n",
                       GetTime().c_str(), params['d'].c_str());
                break;
            }

            case 'h': {
                // 帮助: 可选参数. 打印帮助信息
                Help();
                return FAILED;
            }

            default: {
                printf("%s - ERROR - [XACL]: Parameter is invalid\n",
                       GetTime().c_str());
                return FAILED;
            }
        }
    }

    // 判断模型文件是否存在, 不存在则报错退出
    if (params['m'].empty()) {
        printf("%s - ERROR - [XACL]: Ascend model file (-m) parameter is required\n",
               GetTime().c_str());
        return FAILED;
    }
    if (access(params['m'].c_str(), F_OK) == -1) {
        printf("%s - ERROR - [XACL]: Ascend model file is not exist\n",
               GetTime().c_str());
        return FAILED;
    }

    // 判断Output目录是否存在, 若不存在则创建目录, 创建失败则退出
    if (CreateDir(params['o']) != SUCCESS) return FAILED;

    // 动态Image分档, 动态Batch分档和动态Dims分档以及动态Shape不能同时存在
    if ((!params['y'].empty() && !params['x'].empty()) ||
        (!params['y'].empty() && !params['z'].empty()) ||
        (!params['y'].empty() && !params['v'].empty()) ||
        (!params['x'].empty() && !params['z'].empty()) ||
        (!params['x'].empty() && !params['v'].empty()) ||
        (!params['z'].empty() && !params['v'].empty())) {
        printf("%s - ERROR - [XACL]: Can't set the rank size of batch, image, dims or dynamic shape at the same time\n",
               GetTime().c_str());
        return FAILED;
    }

    // 因为要格式化输出, 要求每个样本最大Loop次数不能超过99次, 实际相关场景也很少, 在此做个限制
    uint32_t loopNum = (int) strtol(params['l'].c_str(), nullptr, 10);
    uint32_t maxLoop = 99;
    if (loopNum > maxLoop) {
        printf("%s - ERROR - [XACL]: Loop number must be less than 99 times\n",
               GetTime().c_str());
        return FAILED;
    }

    return SUCCESS;
}

// 初始化模型
Result InitResource(struct ModelInfo &modelInfo, map<char, string> &params)
{
    aclError ret;
    // 获取Device运行方式, 判断Device合设或者分设
    aclrtRunMode runMode;
    ret = aclrtGetRunMode(&runMode);
    if (ret != ACL_ERROR_NONE) {
        printf("%s - ERROR - [XACL]: Interface of aclrtGetRunMode return failed, error message is:\n%s\n",
               GetTime().c_str(), aclGetRecentErrMsg());
        return FAILED;
    }
    // 获取SOC名称
    string socName = aclrtGetSocName();
    printf("%s - INFO - [XACL]: Current Ascend chipset platform is: %s\n",
           GetTime().c_str(), socName.c_str());
    if (runMode == ACL_DEVICE) {
        params['r'] = "1";
        if (socName == "Ascend610") {
            BuffCfg buffCfg = {{{0}}};
            int driverRet;

            driverRet = halGrpAttach("DEFAULT_MEM_GROUP", 0);
            if (driverRet) {
                printf("%s - ERROR - [XACL]: Interface of halGrpAttach return failed, error code is: %d\n",
                       GetTime().c_str(), driverRet);
                return FAILED;
            }
            driverRet = halBuffInit(&buffCfg);
            if (driverRet) {
                printf("%s - ERROR - [XACL]: Interface of halBuffInit return failed, error code is: %d\n",
                       GetTime().c_str(), driverRet);
                return FAILED;
            }
        }
    } else {
        params['r'] = "0";
    }
    // 初始化ACL
    ret = aclInit((char *) params['d'].c_str());
    int nodeId = (int) strtol(params['n'].c_str(), nullptr, 10);
    if (ret != ACL_ERROR_NONE) {
        printf("%s - ERROR - [XACL]: Interface of aclInit return failed, error message is:\n%s\n",
               GetTime().c_str(), aclGetRecentErrMsg());
        return FAILED;
    }
    // 初始化Device
    ret = aclrtSetDevice(nodeId);
    if (ret != ACL_ERROR_NONE) {
        printf("%s - ERROR - [XACL]: Interface of aclrtSetDevice return failed, error message is:\n%s\n",
               GetTime().c_str(), aclGetRecentErrMsg());
        return FAILED;
    }
    // 初始化上下文
    aclrtContext context;
    ret = aclrtCreateContext(&context, nodeId);
    modelInfo.v_context.push_back(context);
    if (ret != ACL_ERROR_NONE) {
        printf("%s - ERROR - [XACL]: Interface of aclrtCreateContext return failed, error message is:\n%s\n",
               GetTime().c_str(), aclGetRecentErrMsg());
        return FAILED;
    }
    return SUCCESS;
}

// 加载模型
Result LoadModelFromFile(struct ModelInfo &modelInfo, map<char, string> &params)
{
    aclError ret;
    ret = aclmdlLoadFromFile(params['m'].c_str(), &modelInfo.modelId);
    if (ret != ACL_ERROR_NONE) {
        printf("%s - ERROR - [XACL]: Interface of aclmdlLoadFromFile return failed, error message is:\n%s\n",
               GetTime().c_str(), aclGetRecentErrMsg());
        return FAILED;
    }
    return SUCCESS;
}

// 创建模型描述
Result CreateModelDesc(struct ModelInfo &modelInfo)
{
    aclError ret;
    // 创建模型描述
    modelInfo.modelDesc = aclmdlCreateDesc();
    if (modelInfo.modelDesc == nullptr) {
        printf("%s - ERROR - [XACL]: Interface of aclmdlCreateDesc return failed\n",
               GetTime().c_str());
        return FAILED;
    }
    // 获取模型描述
    ret = aclmdlGetDesc(modelInfo.modelDesc, modelInfo.modelId);
    if (ret != ACL_ERROR_NONE) {
        printf("%s - ERROR - [XACL]: Interface of aclmdlGetDesc return failed, error message is:\n%s\n",
               GetTime().c_str(), aclGetRecentErrMsg());
        return FAILED;
    }
    return SUCCESS;
}

// 计算模型输入需要拼接多少个输入文件
Result CalculateInputPerBatch(struct ModelInfo &modelInfo, map<char, string> &params, size_t inputIdx)
{
    if (params['t'] != "empty") {
        // 非空输入下, 获取单个文件的Size
        ifstream binFile(modelInfo.v_allInputFiles[inputIdx][0], ifstream::binary);
        if (!binFile.is_open()) {
            printf("%s - ERROR - [XACL]: Failed to open input file: %s\n",
                   GetTime().c_str(), modelInfo.v_allInputFiles[inputIdx][0].c_str());
            return FAILED;
        }
        binFile.seekg(0, std::ifstream::end);
        uint32_t binFileBufferLen = binFile.tellg();
        binFile.seekg(0, std::ifstream::beg);
        binFile.close();
        printf("%s - INFO - [XACL]: Input %ld size from the input file: %u bytes\n",
               GetTime().c_str(), inputIdx, binFileBufferLen);
        if (modelInfo.v_inputSize[inputIdx] < binFileBufferLen) {
            printf("%s - ERROR - [XACL]: Input %ld file size is larger than the size in the model\n",
                   GetTime().c_str(), inputIdx);
            return FAILED;
        }
        if (modelInfo.v_inputSize[inputIdx] % binFileBufferLen) {
            // 若输入文件大小不能被模型输入大小整除, 则认为输入异常退出
            printf("%s - ERROR - [XACL]: Input %ld file size cannot be divisible by the size in the model\n",
                   GetTime().c_str(), inputIdx);
            return FAILED;
        }
        // 压入所需文件个数 = 模型输入Size / 单个文件Size
        modelInfo.v_filesPerBatch.push_back(modelInfo.v_inputSize[inputIdx] / binFileBufferLen);
    } else {
        // 空输入下不需要处理, 直接以模型输入Size创建全零输入即可
        printf("%s - INFO - [XACL]: Input %ld is empty, malloc size to: %u bytes\n",
               GetTime().c_str(), inputIdx, modelInfo.v_inputSize[inputIdx]);
        modelInfo.v_filesPerBatch.push_back(1);
    }
    return SUCCESS;
}

// 校验各类场景下的输入个数是否合法
Result CheckInputNumber(struct ModelInfo &modelInfo, map<char, string> &params, size_t inputNumbers)
{
    // 非空输入下, 输入文件个数和模型中获取的输入个数(或输入个数减一)必须相同
    if (modelInfo.v_allInputFiles.size() != inputNumbers && params['t'] != "empty") {
        printf("%s - ERROR - [XACL]: Inconsistent input numbers\n",
               GetTime().c_str());
        return FAILED;
    }
    if (!params['x'].empty()) {
        // 动态分辨率分档时, v_imageRank必须有且仅有两个非零输入, 且第一个为H, 第二个为W
        int ten = 10;
        vector<string> v_imageRank;
        Split(params['x'], v_imageRank, ",");
        for (uint32_t Idx = 0; Idx < v_imageRank.size(); ++Idx) {
            if (Idx == 0) {
                modelInfo.heightRank = strtol(v_imageRank[Idx].c_str(), nullptr, ten);
            } else if (Idx == 1) {
                modelInfo.widthRank = strtol(v_imageRank[Idx].c_str(), nullptr, ten);
            } else {
                return FAILED;
            }
        }
        if (modelInfo.heightRank == 0 || modelInfo.widthRank == 0) {
            printf("%s - ERROR - [XACL]: Rank image must give both height and width\n",
                   GetTime().c_str());
            return FAILED;
        }
    }
    return SUCCESS;
}

// 动态Shape场景下计算输入Size
Result CalculateDynamicShapeSize(struct ModelInfo &modelInfo, map<char, string> &params)
{
    aclError ret;
    int ten = 10;
    uint32_t inputCount = 0;
    string inputSizeType;
    vector<string> v_dynamicShape;
    Split(params['v'], v_dynamicShape, ";");
    size_t inputNumbers = aclmdlGetNumInputs(modelInfo.modelDesc);
    // 动态Shape场景非空输入下, 输入文件个数和模型中获取的输入个数必须相同
    if (CheckInputNumber(modelInfo, params, inputNumbers)) return FAILED;
    for (size_t inputIdx = 0; inputIdx < inputNumbers; inputIdx++) {
        // 获取动态Shape每个输入的维度信息
        aclmdlIODims inputDims;
        vector<int64_t> v_realInputShape;
        ret = aclmdlGetInputDims(modelInfo.modelDesc, inputIdx, &inputDims);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclmdlGetInputDims return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
            return FAILED;
        }
        // 输入动态Shape下, 无法获取Size, 此时需要通过输入Shape和Dtype计算输入Size, 并保存Shape信息到v_realInputShape
        uint32_t inputSize;
        aclDataType inputDataType = aclmdlGetInputDataType(modelInfo.modelDesc, inputIdx);
        size_t totalDims = 1;
        vector<string> v_inputShape;
        Split(v_dynamicShape[inputCount], v_inputShape, ",");
        for (auto &dimIdx: v_inputShape) {
            totalDims = totalDims * (int) strtol(dimIdx.c_str(), nullptr, ten);
            v_realInputShape.push_back((int) strtol(dimIdx.c_str(), nullptr, ten));
        }
        inputSize = totalDims * aclDataTypeSize(inputDataType);
        inputSizeType = "shape";
        inputCount++;
        modelInfo.v_inputShape.push_back(v_realInputShape);
        modelInfo.v_inputSize.push_back(inputSize);
        printf("%s - INFO - [XACL]: Input %ld size from the %s: %u bytes\n",
               GetTime().c_str(), inputIdx, inputSizeType.c_str(), inputSize);
        // 通过计算得出的输入Size, 计算当前模型输入需要拼接多少输入文件
        if (CalculateInputPerBatch(modelInfo, params, inputIdx)) return FAILED;
    }
    return SUCCESS;
}

// 动态分辨率分档场景下计算输入Size
Result CalculateRankImageSize(struct ModelInfo &modelInfo, map<char, string> &params)
{
    aclError ret;
    string inputSizeType;
    aclmdlHW image;
    size_t inputNumbers = aclmdlGetNumInputs(modelInfo.modelDesc);
    // 分档场景非空输入下, 输入文件个数是模型中输入个数-1(减去分档输入)
    if (CheckInputNumber(modelInfo, params, inputNumbers - 1)) return FAILED;
    uint32_t imageRank = modelInfo.heightRank * modelInfo.widthRank;
    // Index为预留参数, 当前未使用, 固定设置为-1
    ret = aclmdlGetDynamicHW(modelInfo.modelDesc, -1, &image);
    if (ret != ACL_ERROR_NONE) {
        printf("%s - ERROR - [XACL]: Interface of aclmdlGetDynamicHW return failed, error message is:\n%s\n",
               GetTime().c_str(), aclGetRecentErrMsg());
        return FAILED;
    }
    // 获取最大档位信息
    uint32_t maxImage = image.hw[image.hwCount - 1][0] * image.hw[image.hwCount - 1][1];
    for (size_t inputIdx = 0; inputIdx < inputNumbers; inputIdx++) {
        uint32_t inputSize = aclmdlGetInputSizeByIndex(modelInfo.modelDesc, inputIdx);
        if (inputIdx == inputNumbers - 1) {
            // 动态分辨率分档, 最后一个输入是档位, 不做处理
            modelInfo.v_inputSize.push_back(inputSize);
            return SUCCESS;
        }
        aclmdlIODims inputDims;
        vector<int64_t> v_realInputShape;
        ret = aclmdlGetInputDims(modelInfo.modelDesc, inputIdx, &inputDims);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclmdlGetInputDims return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
            return FAILED;
        }
        // 保存Shape信息到v_realInputShape
        bool widthFlag = false;
        for (size_t dimIdx = 0; dimIdx < inputDims.dimCount; dimIdx++) {
            if (inputDims.dims[dimIdx] == -1) {
                if (!widthFlag) {
                    v_realInputShape.push_back(modelInfo.heightRank);
                    widthFlag = true;
                } else {
                    v_realInputShape.push_back(modelInfo.widthRank);
                    widthFlag = false;
                }
            } else {
                v_realInputShape.push_back(inputDims.dims[dimIdx]);
            }
        }
        // 动态分辨率分档inputSize获取的是最大档位大小, 要和实际分辨率处理一下得到实际输入大小
        inputSize = inputSize / maxImage * imageRank;
        inputSizeType = "shape";
        modelInfo.v_inputShape.push_back(v_realInputShape);
        modelInfo.v_inputSize.push_back(inputSize);
        printf("%s - INFO - [XACL]: Input %ld size from the %s: %u bytes\n",
               GetTime().c_str(), inputIdx, inputSizeType.c_str(), inputSize);
        // 通过计算得出的输入Size, 计算当前模型输入需要拼接多少输入文件
        if (CalculateInputPerBatch(modelInfo, params, inputIdx)) return FAILED;
    }
    return SUCCESS;
}

// 动态Batch分档场景下计算输入Size
Result CalculateRankBatchSize(struct ModelInfo &modelInfo, map<char, string> &params)
{
    aclError ret;
    int ten = 10;
    string inputSizeType;
    aclmdlBatch batch;
    modelInfo.batchRank = (int) strtol(params['y'].c_str(), nullptr, ten);
    size_t inputNumbers = aclmdlGetNumInputs(modelInfo.modelDesc);
    // 分档场景非空输入下, 输入文件个数是模型中输入个数-1(减去分档输入)
    if (CheckInputNumber(modelInfo, params, inputNumbers - 1)) return FAILED;
    ret = aclmdlGetDynamicBatch(modelInfo.modelDesc, &batch);
    if (ret != ACL_ERROR_NONE) {
        printf("%s - ERROR - [XACL]: Interface of aclmdlGetDynamicBatch return failed, error message is:\n%s\n",
               GetTime().c_str(), aclGetRecentErrMsg());
        return FAILED;
    }
    // 获取最大档位信息
    uint32_t maxBatch = batch.batch[batch.batchCount - 1];
    for (size_t inputIdx = 0; inputIdx < inputNumbers; inputIdx++) {
        uint32_t inputSize = aclmdlGetInputSizeByIndex(modelInfo.modelDesc, inputIdx);
        if (inputIdx == inputNumbers - 1) {
            // 动态Batch分档, 最后一个输入是档位, 不做处理
            modelInfo.v_inputSize.push_back(inputSize);
            return SUCCESS;
        }
        aclmdlIODims inputDims;
        vector<int64_t> v_realInputShape;
        ret = aclmdlGetInputDims(modelInfo.modelDesc, inputIdx, &inputDims);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclmdlGetInputDims return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
            return FAILED;
        }
        // 保存Shape信息到v_realInputShape
        for (size_t dimIdx = 0; dimIdx < inputDims.dimCount; dimIdx++) {
            if (inputDims.dims[dimIdx] == -1) {
                v_realInputShape.push_back(modelInfo.batchRank);
            } else {
                v_realInputShape.push_back(inputDims.dims[dimIdx]);
            }
        }
        // 动态Batch分档inputSize获取的是最大档位大小, 要和实际Batch处理一下得到需要的输入大小
        inputSize = inputSize / maxBatch * modelInfo.batchRank;
        inputSizeType = "shape";
        modelInfo.v_inputShape.push_back(v_realInputShape);
        modelInfo.v_inputSize.push_back(inputSize);
        printf("%s - INFO - [XACL]: Input %ld size from the %s: %u bytes\n",
               GetTime().c_str(), inputIdx, inputSizeType.c_str(), inputSize);
        // 通过计算得出的输入Size, 计算当前模型输入需要拼接多少输入文件
        if (CalculateInputPerBatch(modelInfo, params, inputIdx)) return FAILED;
    }
    return SUCCESS;
}

// 动态Dims分档场景下计算输入Size
Result CalculateRankDimsSize(struct ModelInfo &modelInfo, map<char, string> &params)
{
    aclError ret;
    int ten = 10;
    string inputSizeType;
    uint32_t dimCount = 0;
    vector<string> v_dimsRank;
    Split(params['z'], v_dimsRank, ",");
    size_t inputNumbers = aclmdlGetNumInputs(modelInfo.modelDesc);
    // 分档场景非空输入下, 输入文件个数是模型中输入个数-1(减去分档输入)
    if (CheckInputNumber(modelInfo, params, inputNumbers - 1)) return FAILED;
    for (size_t inputIdx = 0; inputIdx < inputNumbers; inputIdx++) {
        uint32_t inputSize = aclmdlGetInputSizeByIndex(modelInfo.modelDesc, inputIdx);
        if (inputIdx == inputNumbers - 1) {
            // 动态Dims分档, 最后一个输入是档位, 不做处理
            modelInfo.v_inputSize.push_back(inputSize);
            modelInfo.dimsRank.dimCount = dimCount;
            return SUCCESS;
        }
        aclmdlIODims inputDims;
        vector<int64_t> v_realInputShape;
        aclDataType inputDataType = aclmdlGetInputDataType(modelInfo.modelDesc, inputIdx);
        ret = aclmdlGetInputDims(modelInfo.modelDesc, inputIdx, &inputDims);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclmdlGetInputDims return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
            return FAILED;
        }
        // 保存Shape信息到v_realInputShape
        size_t totalDims = 1;
        for (size_t dimIdx = 0; dimIdx < inputDims.dimCount; dimIdx++) {
            int64_t rankDimValue = (int) strtol(v_dimsRank[dimCount].c_str(), nullptr, ten);
            totalDims = totalDims * rankDimValue;
            v_realInputShape.push_back(rankDimValue);
            modelInfo.dimsRank.dims[dimCount] = rankDimValue;
            dimCount++;
        }
        // 通过输入Shape和Dtype计算输入Size
        inputSize = totalDims * aclDataTypeSize(inputDataType);
        inputSizeType = "shape";
        modelInfo.v_inputShape.push_back(v_realInputShape);
        modelInfo.v_inputSize.push_back(inputSize);
        printf("%s - INFO - [XACL]: Input %ld size from the %s: %u bytes\n",
               GetTime().c_str(), inputIdx, inputSizeType.c_str(), inputSize);
        // 通过计算得出的输入Size, 计算当前模型输入需要拼接多少输入文件
        if (CalculateInputPerBatch(modelInfo, params, inputIdx)) return FAILED;
    }
    return SUCCESS;
}

// 静态场景下计算输入Size
Result CalculateStaticSize(struct ModelInfo &modelInfo, map<char, string> &params)
{
    aclError ret;
    string inputSizeType;
    // 静态Shape场景
    size_t inputNumbers = aclmdlGetNumInputs(modelInfo.modelDesc);
    // 静态Shape场景非空输入下, 输入文件个数和模型中获取的输入个数必须相同
    if (CheckInputNumber(modelInfo, params, inputNumbers)) return FAILED;
    for (size_t inputIdx = 0; inputIdx < inputNumbers; inputIdx++) {
        aclmdlIODims inputDims;
        vector<int64_t> v_realInputShape;
        ret = aclmdlGetInputDims(modelInfo.modelDesc, inputIdx, &inputDims);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclmdlGetInputDims return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
            return FAILED;
        }
        uint32_t inputSize = aclmdlGetInputSizeByIndex(modelInfo.modelDesc, inputIdx);
        // 保存Shape信息到v_realInputShape
        for (size_t dimIdx = 0; dimIdx < inputDims.dimCount; dimIdx++) {
            v_realInputShape.push_back(inputDims.dims[dimIdx]);
        }
        modelInfo.v_inputShape.push_back(v_realInputShape);
        modelInfo.v_inputSize.push_back(inputSize);
        inputSizeType = "model";
        printf("%s - INFO - [XACL]: Input %ld size from the %s: %u bytes\n",
               GetTime().c_str(), inputIdx, inputSizeType.c_str(), inputSize);
        // 计算当前模型输入需要拼接多少输入文件
        if (CalculateInputPerBatch(modelInfo, params, inputIdx)) return FAILED;
    }
    return SUCCESS;
}

// 计算真实输入Size
Result CalculateInputSize(struct ModelInfo &modelInfo, map<char, string> &params)
{
    // 获取输入
    GenerateInputList(modelInfo, params);

    size_t inputFiles = modelInfo.v_allInputFiles[0].size();
    // 针对不同场景, 确定inputSize大小, 判断是否需要拼接输入, 并给出拼接文件个数
    if (!params['v'].empty()) {
        if (CalculateDynamicShapeSize(modelInfo, params)) return FAILED;
    } else if (!params['x'].empty()) {
        if (CalculateRankImageSize(modelInfo, params)) return FAILED;
    } else if (!params['y'].empty()) {
        if (CalculateRankBatchSize(modelInfo, params)) return FAILED;
    } else if (!params['z'].empty()) {
        if (CalculateRankDimsSize(modelInfo, params)) return FAILED;
    } else {
        if (CalculateStaticSize(modelInfo, params)) return FAILED;
    }

    // 判断多输入场景下, 如果每个输入Batch不同, 例如第一个输入包含两份数据, 第二个输入仅包含一份输入, 该场景不处理直接报错退出
    size_t maxFilesPerBatch = *max_element(modelInfo.v_filesPerBatch.begin(), modelInfo.v_filesPerBatch.end());
    size_t minFilesPerBatch = *min_element(modelInfo.v_filesPerBatch.begin(), modelInfo.v_filesPerBatch.end());
    if ((maxFilesPerBatch - minFilesPerBatch) > 0) {
        printf("%s - ERROR - [XACL]: Inconsistent input batches\n",
               GetTime().c_str());
        return FAILED;
    }

    /* 如果实际输入文件个数和实际所需输入个数不能整除, 则按最后一个文件补齐余数
    由于输出文件与输入文件名相同, 该处理在保证计算不会引入异常数据的同时, 能保证输出文件不增加(同名输出覆盖) */
    uint32_t remainder = modelInfo.v_filesPerBatch[0] - inputFiles % modelInfo.v_filesPerBatch[0];
    if (remainder == modelInfo.v_filesPerBatch[0]) remainder = 0;
    for (; remainder > 0; remainder--) {
        for (auto &v_allInputFile: modelInfo.v_allInputFiles) {
            v_allInputFile.push_back(v_allInputFile[inputFiles - 1]);
        }
    }

    return SUCCESS;
}

// 创建全零输入
Result CreateZeroDataBuffer(struct ModelInfo &modelInfo, map<char, string> &params, size_t inputIdx)
{
    aclError ret;
    void *inputBuffer = nullptr;
    // 以ACL_MEM_MALLOC_HUGE_FIRST方式申请Device地址
    ret = aclrtMalloc(&inputBuffer, modelInfo.v_inputSize[inputIdx], ACL_MEM_MALLOC_HUGE_FIRST);
    if (ret != ACL_ERROR_NONE) {
        printf("%s - ERROR - [XACL]: Interface of aclrtMalloc return failed, error message is:\n%s\n",
               GetTime().c_str(), aclGetRecentErrMsg());
        return FAILED;
    }
    if (params['r'] == "0") {
        // DC场景先申请Host地址
        void *binFileBufferData = nullptr;
        ret = aclrtMallocHost(&binFileBufferData, modelInfo.v_inputSize[inputIdx]);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclrtMallocHost return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
            return FAILED;
        }
        // 数据附零
        aclrtMemset(binFileBufferData, modelInfo.v_inputSize[inputIdx], 0, modelInfo.v_inputSize[inputIdx]);
        // 将Host数据搬运到Device
        ret = aclrtMemcpy(inputBuffer, modelInfo.v_inputSize[inputIdx], binFileBufferData,
                          modelInfo.v_inputSize[inputIdx], ACL_MEMCPY_HOST_TO_DEVICE);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclrtMemcpy return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
            return FAILED;
        }
        // 数据搬运到Device后, 释放Host数据
        ret = aclrtFreeHost(binFileBufferData);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclrtFreeHost return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
            return FAILED;
        }
    } else {
        // MDC场景数据附零
        aclrtMemset(inputBuffer, modelInfo.v_inputSize[inputIdx], 0, modelInfo.v_inputSize[inputIdx]);
    }
    // 创建模型输入Dataset
    aclDataBuffer *inputData = aclCreateDataBuffer(inputBuffer, modelInfo.v_inputSize[inputIdx]);
    if (inputData == nullptr) {
        printf("%s - ERROR - [XACL]: Interface of aclCreateDataBuffer return failed\n",
               GetTime().c_str());
        return FAILED;
    }
    ret = aclmdlAddDatasetBuffer(modelInfo.input, inputData);
    if (ret != ACL_ERROR_NONE) {
        printf("%s - ERROR - [XACL]: Interface of aclmdlAddDatasetBuffer return failed, error message is:\n%s\n",
               GetTime().c_str(), aclGetRecentErrMsg());
        return FAILED;
    }
    return SUCCESS;
}

// 创建文件输入
Result CreateFileDataBuffer(struct ModelInfo &modelInfo, map<char, string> &params, size_t inputIdx, uint32_t fileIdx)
{
    aclError ret;
    void *inputBuffer = nullptr;
    // 以输入Size ACL_MEM_MALLOC_HUGE_FIRST方式申请Device地址
    ret = aclrtMalloc(&inputBuffer, modelInfo.v_inputSize[inputIdx], ACL_MEM_MALLOC_HUGE_FIRST);
    if (ret != ACL_ERROR_NONE) {
        printf("%s - ERROR - [XACL]: Interface of aclrtMalloc return failed, error message is:\n%s\n",
               GetTime().c_str(), aclGetRecentErrMsg());
        return FAILED;
    }
    // 创建模型输入Dataset
    aclDataBuffer *inputData = aclCreateDataBuffer(inputBuffer, modelInfo.v_inputSize[inputIdx]);
    if (inputData == nullptr) {
        printf("%s - ERROR - [XACL]: Interface of aclCreateDataBuffer return failed\n",
               GetTime().c_str());
        return FAILED;
    }
    ret = aclmdlAddDatasetBuffer(modelInfo.input, inputData);
    if (ret != ACL_ERROR_NONE) {
        printf("%s - ERROR - [XACL]: Interface of aclmdlAddDatasetBuffer return failed, error message is:\n%s\n",
               GetTime().c_str(), aclGetRecentErrMsg());
        return FAILED;
    }
    // 判断是否是分档场景, 如果是, 则最后一个档位输入不需要读取文件分配地址
    if (!params['x'].empty() || !params['y'].empty() || !params['z'].empty()) {
        if (inputIdx == modelInfo.v_allInputFiles.size()) return SUCCESS;
    }
    // 获取输入Dataset首地址
    void *batchDst = aclGetDataBufferAddr(inputData);
    uint32_t pos = 0;
    // 按所需输入文件个数循环读取文件列表
    for (size_t i = 0; i < modelInfo.v_filesPerBatch[inputIdx]; i++) {
        string fileName = modelInfo.v_allInputFiles[inputIdx][fileIdx + i];
        ifstream binFile(fileName, ifstream::binary);
        if (!binFile.is_open()) {
            printf("%s - ERROR - [XACL]: Failed to open input file: %s\n",
                   GetTime().c_str(), fileName.c_str());
            return FAILED;
        }
        binFile.seekg(0, std::ifstream::end);
        uint32_t singleFileSize = binFile.tellg();
        binFile.seekg(0, std::ifstream::beg);
        if (params['r'] == "0") {
            void *inputHostBuff = nullptr;
            // 先申请Host地址
            ret = aclrtMallocHost(&inputHostBuff, singleFileSize);
            if (ret != ACL_ERROR_NONE) {
                printf("%s - ERROR - [XACL]: Interface of aclrtMallocHost return failed, error message is:\n%s\n",
                       GetTime().c_str(), aclGetRecentErrMsg());
                binFile.close();
                return FAILED;
            }
            binFile.read(static_cast<char *>(inputHostBuff), singleFileSize);
            binFile.close();
            // 将Host数据搬运到Device
            ret = aclrtMemcpy((uint8_t *) batchDst + pos, singleFileSize, inputHostBuff, singleFileSize,
                              ACL_MEMCPY_HOST_TO_DEVICE);
            if (ret != ACL_ERROR_NONE) {
                printf("%s - ERROR - [XACL]: Interface of aclrtMemcpy return failed, error message is:\n%s\n",
                       GetTime().c_str(), aclGetRecentErrMsg());
                ret = aclrtFreeHost(inputHostBuff);
                if (ret != ACL_ERROR_NONE) {
                    printf("%s - ERROR - [XACL]: Interface of aclrtFreeHost return failed, error message is:\n%s\n",
                           GetTime().c_str(), aclGetRecentErrMsg());
                }
                return FAILED;
            }
            pos += singleFileSize;
            ret = aclrtFreeHost(inputHostBuff);
            if (ret != ACL_ERROR_NONE) {
                printf("%s - ERROR - [XACL]: Interface of aclrtFreeHost return failed, error message is:\n%s\n",
                       GetTime().c_str(), aclGetRecentErrMsg());
            }
        } else {
            void *inputDevBuff = (uint8_t *) batchDst + pos;
            binFile.read(static_cast<char *>(inputDevBuff), singleFileSize);
            binFile.close();
            pos += singleFileSize;
        }
    }
    return SUCCESS;
}

// 创建输入
Result CreateInput(struct ModelInfo &modelInfo, map<char, string> &params, uint32_t fileIdx)
{
    modelInfo.input = aclmdlCreateDataset();
    if (modelInfo.input == nullptr) {
        printf("%s - ERROR - [XACL]: Interface of aclmdlCreateDataset return failed\n",
               GetTime().c_str());
        return FAILED;
    }
    if (params['t'] == "empty") {
        // 不指定输入, 创建全零输入
        printf("%s - INFO - [XACL]: Input type is empty, create all zero inputs\n",
               GetTime().c_str());
        for (size_t inputIdx = 0; inputIdx < modelInfo.v_inputSize.size(); inputIdx++) {
            if (CreateZeroDataBuffer(modelInfo, params, inputIdx)) return FAILED;
        }
    } else {
        // 目录输入
        for (size_t inputIdx = 0; inputIdx < modelInfo.v_inputSize.size(); inputIdx++) {
            if (CreateFileDataBuffer(modelInfo, params, inputIdx, fileIdx)) return FAILED;
        }
    }
    return SUCCESS;
}

// 创建输入描述信息
Result CreateInputDesc(struct ModelInfo &modelInfo, map<char, string> &params)
{
    for (uint32_t inIdx = 0; inIdx < modelInfo.v_inputShape.size(); ++inIdx) {
        aclError ret;
        int numDims = int(modelInfo.v_inputShape[inIdx].size());
        int64_t shape[numDims];
        for (int dimIdx = 0; dimIdx < numDims; dimIdx++) {
            shape[dimIdx] = modelInfo.v_inputShape[inIdx][dimIdx];
        }
        // ACL_FLOAT16 与 ACL_FORMAT_NHWC暂不生效
        aclTensorDesc *inputDesc;
        inputDesc = aclCreateTensorDesc(ACL_FLOAT16, numDims, shape, ACL_FORMAT_NHWC);
        ret = aclmdlSetDatasetTensorDesc(modelInfo.input, inputDesc, inIdx);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - INFO - [XACL]: Interface of aclmdlSetDatasetTensorDesc return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
            return FAILED;
        }
        modelInfo.v_inputDesc.push_back(inputDesc);
    }
    return SUCCESS;
}

// 创建输出
Result CreateOutput(struct ModelInfo &modelInfo, map<char, string> &params)
{
    aclError ret;
    void *outputBuffer = nullptr;
    size_t defaultSize = 81920000;
    modelInfo.output = aclmdlCreateDataset();
    if (modelInfo.output == nullptr) {
        printf("%s - ERROR - [XACL]: Interface of aclmdlCreateDataset return failed\n",
               GetTime().c_str());
        return FAILED;
    }

    size_t outputSize = aclmdlGetNumOutputs(modelInfo.modelDesc);
    for (size_t outIdx = 0; outIdx < outputSize; ++outIdx) {
        // 获取输出Size
        size_t bufferSize = aclmdlGetOutputSizeByIndex(modelInfo.modelDesc, outIdx);
        if (bufferSize == 0) {
            // 若输出无法获取, 则以默认值赋值
            printf("%s - INFO - [XACL]: Output size is zero, malloc 81920000 bytes\n",
                   GetTime().c_str());
            bufferSize = defaultSize;
        }
        // 申请Device地址
        ret = aclrtMalloc(&outputBuffer, bufferSize, ACL_MEM_MALLOC_HUGE_FIRST);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclrtMalloc return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
            return FAILED;
        }
        // 创建模型输出Dataset
        aclDataBuffer *outputData = aclCreateDataBuffer(outputBuffer, bufferSize);
        if (outputData == nullptr) {
            printf("%s - ERROR - [XACL]: Interface of aclCreateDataBuffer return failed\n",
                   GetTime().c_str());
            return FAILED;
        }
        ret = aclmdlAddDatasetBuffer(modelInfo.output, outputData);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclmdlAddDatasetBuffer return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
            return FAILED;
        }
    }
    return SUCCESS;
}

// 获取分档输入序号
Result GetRankIndex(struct ModelInfo &modelInfo, size_t &rankIndex)
{
    aclError ret;
    ret = aclmdlGetInputIndexByName(modelInfo.modelDesc, ACL_DYNAMIC_TENSOR_NAME, &rankIndex);
    if (ret != ACL_ERROR_NONE) {
        printf("%s - ERROR - [XACL]: Interface of aclmdlGetInputIndexByName return failed, error message is:\n%s\n",
               GetTime().c_str(), aclGetRecentErrMsg());
        return FAILED;
    }
    return SUCCESS;
}

// 设置分档信息
Result SetRankInfo(struct ModelInfo &modelInfo, map<char, string> &params)
{
    aclError ret;
    size_t rankIndex;
    // 设置动态分辨率分档
    if (!params['x'].empty()) {
        if (GetRankIndex(modelInfo, rankIndex)) return FAILED;
        ret = aclmdlSetDynamicHWSize(modelInfo.modelId, modelInfo.input, rankIndex,
                                     modelInfo.heightRank, modelInfo.widthRank);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclmdlSetDynamicHWSize return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
            return FAILED;
        }
    }
    // 设置动态Batch分档
    if (!params['y'].empty()) {
        if (GetRankIndex(modelInfo, rankIndex)) return FAILED;
        ret = aclmdlSetDynamicBatchSize(modelInfo.modelId, modelInfo.input, rankIndex, modelInfo.batchRank);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclmdlSetDynamicBatchSize return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
            return FAILED;
        }
    }
    // 设置动态Dims分档
    if (!params['z'].empty()) {
        if (GetRankIndex(modelInfo, rankIndex)) return FAILED;
        ret = aclmdlSetInputDynamicDims(modelInfo.modelId, modelInfo.input, rankIndex, &modelInfo.dimsRank);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclmdlSetInputDynamicDims return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
            return FAILED;
        }
    }
    return SUCCESS;
}

// Dump输出
Result DumpOutput(struct ModelInfo &modelInfo, map<char, string> &params, size_t outIndex, uint32_t fileIdx)
{
    aclError ret;
    uint32_t totalLen;
    string outputSizeType;

    // 从aclGetTensorDescSize接口直接获取OutputSize, 仅适用于动态Shape场景
    aclTensorDesc *outputDesc = aclmdlGetDatasetTensorDesc(modelInfo.output, outIndex);
    aclDataBuffer *dataBuffer = aclmdlGetDatasetBuffer(modelInfo.output, outIndex);
    void *data = aclGetDataBufferAddr(dataBuffer);
    totalLen = aclGetTensorDescSize(outputDesc);
    // 此时获取的输出Size标记为"Tensor"方式获取
    outputSizeType = "tensor";

    // 从接口直接获取OutputSize, 适用于所有场景, 但是动态分档/动态Shape场景获取的是最大Size
    if (totalLen == 0) {
        totalLen = aclGetDataBufferSizeV2(dataBuffer);
        // 此时获取的输出Size标记为"Model"方式获取
        outputSizeType = "model";
    }

    // 若能够从接口获取OutputShape和DType, 则手工计算OutputSize, 此Size为真实Size
    aclDataType outputDataType = aclmdlGetOutputDataType(modelInfo.modelDesc, outIndex);
    aclmdlIODims outputDims;
    ret = aclmdlGetCurOutputDims(modelInfo.modelDesc, outIndex, &outputDims);
    if (ret == ACL_ERROR_NONE) {
        size_t totalDims = 1;
        for (size_t dimIdx = 0; dimIdx < outputDims.dimCount; dimIdx++) {
            totalDims = totalDims * outputDims.dims[dimIdx];
        }
        totalLen = totalDims * aclDataTypeSize(outputDataType);
        // 此时获取的输出Size标记为"Shape"方式获取
        outputSizeType = "shape";
    }

    // 最终Size赋值为上述三种场景的最佳Size
    printf("%s - INFO - [XACL]: Output %zu size from the %s: %u bytes\n",
           GetTime().c_str(), outIndex, outputSizeType.c_str(), totalLen);

    string outputIdxStr = FormatInt(to_string(outIndex), 2);
    if (params['r'] == "0") {
        // 申请Host地址
        void *outHostBuff = nullptr;
        ret = aclrtMallocHost(&outHostBuff, totalLen);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclrtMallocHost return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
            return FAILED;
        }
        // 将Device数据搬运到Host
        ret = aclrtMemcpy(outHostBuff, totalLen, data, totalLen, ACL_MEMCPY_DEVICE_TO_HOST);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclrtMemcpy return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
            return FAILED;
        }
        if (params['t'] != "empty") {
            // 非空输入时, 按照输入文件名写输出文件
            uint32_t singleLen = totalLen / modelInfo.v_filesPerBatch[0];
            for (size_t i = 0; i < modelInfo.v_filesPerBatch[0]; i++) {
                string firstInputName = GetInputName(modelInfo.v_allInputFiles[0][i + fileIdx]);
                string outputPrefix = params['o'] + "output_" + firstInputName + "_";
                string outputFileName;
                outputFileName.append(outputPrefix).append(outputIdxStr).append(".bin");
                FILE *fop = fopen(outputFileName.c_str(), "wb+");
                fwrite((uint8_t *) outHostBuff + (i * singleLen), singleLen, sizeof(char), fop);
                fclose(fop);
            }
        } else {
            // 空输入时, 输出文件直接命名为"output_all_zero_idx.bin"
            string outputPrefix = params['o'] + "output_all_zero_";
            string outputFileName;
            outputFileName.append(outputPrefix).append(outputIdxStr).append(".bin");
            FILE *fop = fopen(outputFileName.c_str(), "wb+");
            fwrite((uint8_t *) outHostBuff, totalLen, sizeof(char), fop);
            fclose(fop);
        }
        // 写文件完后释放Host内存
        ret = aclrtFreeHost(outHostBuff);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclrtFreeHost return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
            return FAILED;
        }
    } else {
        void *outHostBuff = nullptr;
        // 申请Device地址
        ret = aclrtMalloc(&outHostBuff, totalLen, ACL_MEM_MALLOC_HUGE_FIRST);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclrtMalloc return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
            return FAILED;
        }
        // 将Device数据搬运到Device
        ret = aclrtMemcpy(outHostBuff, totalLen, data, totalLen, ACL_MEMCPY_DEVICE_TO_DEVICE);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclrtMemcpy return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
            return FAILED;
        }
        if (params['t'] != "empty") {
            // 非空输入时, 按照输入文件名写输出文件
            uint32_t singleLen = totalLen / modelInfo.v_filesPerBatch[0];
            for (size_t i = 0; i < modelInfo.v_filesPerBatch[0]; i++) {
                string firstInputName = GetInputName(modelInfo.v_allInputFiles[0][i + fileIdx]);
                string outputPrefix = params['o'] + "output_" + firstInputName + "_";
                string outputFileName;
                outputFileName.append(outputPrefix).append(outputIdxStr).append(".bin");
                FILE *fop = fopen(outputFileName.c_str(), "wb+");
                fwrite((uint8_t *) outHostBuff + (i * singleLen), singleLen, sizeof(char), fop);
                fclose(fop);
            }
        } else {
            // 空输入时, 输出文件直接命名为"output_all_zero_idx.bin"
            string outputPrefix = params['o'] + "output_all_zero_";
            string outputFileName;
            outputFileName.append(outputPrefix).append(outputIdxStr).append(".bin");
            FILE *fop = fopen(outputFileName.c_str(), "wb+");
            fwrite((uint8_t *) outHostBuff, totalLen, sizeof(char), fop);
            fclose(fop);
        }
        // 写文件完后释放Device内存
        ret = aclrtFree(outHostBuff);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclrtFree return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());
            return FAILED;
        }
    }
    return SUCCESS;
}

// 模型执行
Result Execute(struct ModelInfo &modelInfo, map<char, string> &params, uint32_t fileIdx)
{
    if (SetRankInfo(modelInfo, params)) return FAILED;
    struct timeval startTimeStamp{};
    struct timeval endTimeStamp{};
    double totalTime = 0.0;
    double costTime;
    double startTime;
    double endTime;
    double thousand = 1000.0;
    double million = 1000000.0;
    // 按loop入参循环执行
    uint32_t loopNum = (int) strtol(params['l'].c_str(), nullptr, 10);
    for (uint32_t loopIdx = 0; loopIdx < loopNum; ++loopIdx) {
        aclError ret;
        // AICore ERROR异常回调函数
        aclrtSetExceptionInfoCallback(ExceptionCallBack);
        gettimeofday(&startTimeStamp, nullptr);
        ret = aclmdlExecute(modelInfo.modelId, modelInfo.input, modelInfo.output);
        gettimeofday(&endTimeStamp, nullptr);
        if (ret != ACL_ERROR_NONE) {
            printf("%s - ERROR - [XACL]: Interface of aclmdlExecute return failed, error message is:\n%s\n",
                   GetTime().c_str(), aclGetRecentErrMsg());

            printf("%s - ERROR - [XACL]: Run acl model failed\n",
                   GetTime().c_str());
            return FAILED;
        } else {
            printf("%s - INFO - [XACL]: Run acl model success\n",
                   GetTime().c_str());
        }
        costTime = ((double) (endTimeStamp.tv_sec - startTimeStamp.tv_sec) * million +
                    (double) (endTimeStamp.tv_usec - startTimeStamp.tv_usec)) / thousand;
        startTime = ((double) startTimeStamp.tv_sec * million + (double) startTimeStamp.tv_usec) / thousand;
        endTime = ((double) endTimeStamp.tv_sec * million + (double) endTimeStamp.tv_usec) / thousand;
        totalTime += costTime;
        modelInfo.v_totalTime.push_back(costTime);
        string loopStr = FormatInt(to_string(loopIdx + 1), 2);
        printf("%s - INFO - [XACL]: Loop %s, start timestamp %4.0f, end timestamp %4.0f, cost time %4.2f ms\n",
               GetTime().c_str(), loopStr.c_str(), startTime, endTime, costTime);
        // 执行完成后打印硬件状态
        GetHardwareStatus(params);
    }
    printf("%s - INFO - [XACL]: Single step average inference time of %d loops: %f ms\n",
           GetTime().c_str(), loopNum, (totalTime / loopNum));
    // 输出仅保存最后一次
    for (size_t outIndex = 0; outIndex < aclmdlGetDatasetNumBuffers(modelInfo.output); ++outIndex) {
        if (DumpOutput(modelInfo, params, outIndex, fileIdx)) return FAILED;
    }
    // 销毁输入输出
    ResetDataset(modelInfo);
    return SUCCESS;
}

// 写模型执行性能结果
Result WriteResult(struct ModelInfo &modelInfo, map<char, string> &params)
{
    uint32_t allRunNum = modelInfo.v_totalTime.size();
    uint32_t loopNum = (int) strtol(params['l'].c_str(), nullptr, 10);
    uint32_t stepNumber = allRunNum / loopNum;
    double totalSamplesTime = accumulate(begin(modelInfo.v_totalTime), end(modelInfo.v_totalTime), 0.0);
    // 如果执行次数超过1次, 去掉首个Step
    if (allRunNum > 1) {
        totalSamplesTime = totalSamplesTime - modelInfo.v_totalTime[0];
        allRunNum = allRunNum - 1;
    }
    // 均值
    double meanTime = totalSamplesTime / allRunNum;
    // 方差
    double variance = 0.0;
    int two = 2;
    for (auto &v_samplesTime: modelInfo.v_totalTime) {
        variance += pow((v_samplesTime - meanTime), two);
    }
    variance = variance / allRunNum;
    // 标准差
    double standardDeviation = sqrt(variance);
    printf("%s - INFO - [XACL]: Total %d steps average inference time: %f ms, variance: %f, standard deviation: %f\n",
           GetTime().c_str(), stepNumber, meanTime, variance, standardDeviation);

    vector<string> v_modelFile;
    Split(params['m'], v_modelFile, "/");
    string modelName = v_modelFile[v_modelFile.size() - 1];

    vector<string> v_modelName;
    Split(modelName, v_modelName, ".");
    string resultFileName = params['o'] + v_modelName[0] + "_performance.txt";

    ofstream resultFile(resultFileName.c_str(), ofstream::out);
    if (!resultFile) {
        printf("%s - INFO - [XACL]: Open acl result file failed\n",
               GetTime().c_str());
        return FAILED;
    } else {
        printf("%s - INFO - [XACL]: Write acl result to file %s\n",
               GetTime().c_str(), resultFileName.c_str());
    }

    resultFile << "Total " << stepNumber << " steps average inference time: ";
    resultFile << meanTime << " ms, variance: " << variance << " , standard deviation: " << standardDeviation << endl;
    resultFile.close();

    return SUCCESS;
}

// 销毁资源
Result DestroyResource(struct ModelInfo &modelInfo, map<char, string> &params)
{
    // 销毁模型描述
    if (modelInfo.modelDesc != nullptr) {
        DestroyModelDesc(modelInfo);
        DestroyModel(modelInfo);
    }
    // 销毁输入输出
    ResetDataset(modelInfo);
    // 销毁上下文
    DestroyContext(modelInfo);
    // 销毁Device
    DestroyDevice(params);
    // 销毁ACL
    DestroyACL();
    return SUCCESS;
}
